package com.dyna.digit.flowable.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.dyna.core.exception.CustomException;
import com.dyna.core.utils.ObjectUtil;
import com.dyna.core.utils.PageUtil;
import com.dyna.digit.flowable.constants.DigitFlowConstant;
import com.dyna.digit.flowable.domain.DefinitionsInfo;
import com.dyna.digit.flowable.domain.DeploymentEntity;
import com.dyna.digit.flowable.domain.DigitTask;
import com.dyna.digit.flowable.domain.param.TaskSubmitParam;
import com.dyna.digit.flowable.domain.request.TaskReq;
import com.dyna.digit.flowable.domain.vo.FlowHistoryVo;
import com.dyna.digit.flowable.service.DigitFlowableService;
import com.dyna.digit.process.domain.entity.DigitProject;
import com.dyna.digit.process.service.DigitProjectService;
import com.dyna.domain.req.PageParam;
import com.dyna.domain.resp.PageResult;
import com.dyna.enums.DigitFlowStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.engine.*;
import org.flowable.engine.repository.*;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author zhoucaiwang
 */

@Slf4j
@Service
public class DigitFlowableServiceImpl implements DigitFlowableService {

    private final ProcessEngine processEngine;
    private final DigitProjectService digitProjectService;

    @Autowired
    public DigitFlowableServiceImpl(ProcessEngine processEngine, DigitProjectService digitProjectService) {
        this.processEngine = processEngine;
        this.digitProjectService = digitProjectService;
    }

    @Override
    public String deploy(String bpmnFile,String flowName) {
        // 流程部署
        RepositoryService repositoryService = processEngine.getRepositoryService();
        DeploymentBuilder deployment = repositoryService.createDeployment();
        // 部署流程图
        bpmnFile = StrUtil.format("template/bpmn/{}.xml", bpmnFile);
        Deployment deploy = deployment.addClasspathResource(bpmnFile).name(flowName).deploy();
        // 流程id
        return deploy.getId();
    }

    @Override
    public PageResult<DeploymentEntity> queryDeploys(String name) {
        PageParam pageParam = PageUtil.getPageParam();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        DeploymentQuery deploymentQuery = repositoryService.createDeploymentQuery();

        if (StrUtil.isNotBlank(name)) {
            deploymentQuery.deploymentName(name);
        }

        long count = deploymentQuery.count();
        List<Deployment> list = deploymentQuery.orderByDeploymentTime().desc().listPage((pageParam.getPageNum() - 1) * pageParam.getPageSize() , pageParam.getPageSize());
        List<DeploymentEntity> deploymentEntities = list.stream().map(it -> BeanUtil.copyProperties(it, DeploymentEntity.class)).collect(Collectors.toList());
        return new PageResult<>(deploymentEntities,count);
    }

    @Override
    public DefinitionsInfo definition(String deployId) {
        // 流程定义
        RepositoryService repositoryService = processEngine.getRepositoryService();
        ProcessDefinitionQuery definitionQuery = repositoryService.createProcessDefinitionQuery();
        if (StrUtil.isNotBlank(deployId)) {
            definitionQuery.deploymentId(deployId);
        }
        ProcessDefinition processDefinition = definitionQuery.orderByDeploymentId().desc().singleResult();
        return BeanUtil.copyProperties(processDefinition, DefinitionsInfo.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String startDefinition(TaskReq taskReq) {
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //创建工单任务
        Map<String, Object> variable = taskReq.getFlowUserList().stream().collect(Collectors.toMap(TaskReq.FlowUser::getAssigneeVarName, TaskReq.FlowUser::getUserId));
        variable.put("projectId", taskReq.getProjectId());
        variable.put("projectName", taskReq.getProjectName());
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(taskReq.getProcessDefinitionKey(), variable);
        if (ObjectUtil.isNotNull(processInstance.getId())) {
            // 修改当前流程状态
            LambdaUpdateWrapper<DigitProject> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
            lambdaUpdateWrapper.set(DigitProject::getFlowStatus, DigitFlowStatusEnum.DEPT_AUDIT.getStatus());
            lambdaUpdateWrapper.eq(DigitProject::getProjectId, taskReq.getProjectId());
            digitProjectService.update(lambdaUpdateWrapper);
        }
        // 开始申报-> 进入部门联审
        Boolean startDeclare = digitProjectService.startDeclare(taskReq.getProjectId());
        log.info("start declare result : {}", startDeclare);
        return processInstance.getId();
    }

    @Override
    public PageResult<DigitTask> queryTask(String key,String assignee, Integer projectId, String projectName) {
        PageParam pageParam = PageUtil.getPageParam();
        List<DigitTask> digitTaskList = new ArrayList<>();

        //查询任务
        TaskService taskService = processEngine.getTaskService();
        TaskQuery taskQuery = taskService.createTaskQuery();
        if (StrUtil.isNotBlank(assignee)) {
            taskQuery.taskAssignee(assignee);
        }
        TaskQuery query = taskQuery.processDefinitionKey(key);
        if (ObjectUtil.isNotNull(projectId)) {
            query.processVariableValueEquals("projectId", projectId);
        }
        if (StrUtil.isNotBlank(projectName)) {
            query.processVariableValueLike("projectName", "%" + projectName + "%");
        }

        // 任务数量
        long count = query.count();
        // 任务
        List<Task> tasks = query.includeProcessVariables().orderByTaskCreateTime().desc().listPage((pageParam.getPageNum() - 1) * pageParam.getPageSize() , pageParam.getPageSize());
        // 解析任务
        for (Task task : tasks) {
            Map<String, Object> processVariables = task.getProcessVariables();
            try {
                DigitTask digitTask = JSON.parseObject(JSON.toJSONString(processVariables), DigitTask.class);
                digitTask.setId(task.getId());
                digitTask.setAssignee(task.getAssignee());
                digitTask.setName(task.getName());
                digitTask.setCreateTime(task.getCreateTime());
                digitTask.setProcessInstanceId(task.getProcessInstanceId());
                digitTask.setProcessDefinitionId(task.getProcessDefinitionId());
                digitTask.setTaskDefinitionKey(task.getTaskDefinitionKey());
                digitTaskList.add(digitTask);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
        return new PageResult<>(digitTaskList, count);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void compileTask(String taskId, TaskSubmitParam taskSubmitParam) {
        try {
            TaskService taskService = processEngine.getTaskService();
            Map<String, Object> map = Convert.toMap(String.class, Object.class, taskSubmitParam);
            map.put(DigitFlowConstant.DIGIT_FLOW_TASK_RESULT + taskId, taskSubmitParam.getStatus());
            // 完成任务
            taskService.complete(taskId,map);
        }catch (FlowableObjectNotFoundException e) {
            throw new CustomException("未查询到该任务");
        }
        // 修改当前流程状态
        LambdaUpdateWrapper<DigitProject> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.set(DigitProject::getFlowStatus, taskSubmitParam.getFlowStatus());
        lambdaUpdateWrapper.eq(DigitProject::getProjectId, taskSubmitParam.getProjectId());
        digitProjectService.update(lambdaUpdateWrapper);
    }

    @Override
    public PageResult<FlowHistoryVo> queryHistoryTask(String id) {
        PageParam pageParam = PageUtil.getPageParam();
        List<FlowHistoryVo> historyVos = new ArrayList<>();
        HistoryService historyService = processEngine.getHistoryService();
        HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery();
        long count = historicTaskInstanceQuery.finished().count();
        List<HistoricTaskInstance> list = historicTaskInstanceQuery.includeProcessVariables()
                .finished()
                .orderByTaskCreateTime()
                .desc()
                .listPage((pageParam.getPageNum() - 1) * pageParam.getPageSize() , pageParam.getPageSize());
        for (HistoricTaskInstance historicTaskInstance : list) {
            Map<String, Object> processVariables = historicTaskInstance.getProcessVariables();
            try {
                FlowHistoryVo flowHistoryVo = BeanUtil.copyProperties(historicTaskInstance, FlowHistoryVo.class);
                FlowHistoryVo.HistoryVariables historyVariables = JSON.parseObject(JSON.toJSONString(processVariables), FlowHistoryVo.HistoryVariables.class);
                // 设置变量审批结果
                Object taskId = processVariables.get(historicTaskInstance.getAssignee());
                if (taskId != null) {
                    Object result = processVariables.get(DigitFlowConstant.DIGIT_FLOW_TASK_RESULT + taskId);
                    historyVariables.setStatus(Convert.toInt(result));
                }
                flowHistoryVo.setHistoryVariables(historyVariables);
                historyVos.add(flowHistoryVo);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
        return new PageResult<>(historyVos,count);
    }



    @Override
    public void delTask(String processInstanceId,String reason) {
        /*
        1. 停止相关的流程实例：在删除任务之前，你需要停止与该任务相关的流程实例。你可以通过调用Flowable引擎的`runtimeService`对象的`deleteProcessInstance`方法来停止流程实例。这将导致相关任务被取消并且可以被删除。
        2. 检查相关的流程定义：如果任务无法被删除，可能是因为相关的流程定义被部署在Flowable引擎中。你需要先删除与任务相关的流程定义，然后再删除任务。你可以使用`repositoryService`对象的`deleteDeployment`方法来删除流程定义。
         */
        RuntimeService runtimeService = processEngine.getRuntimeService();
        runtimeService.deleteProcessInstance(processInstanceId,reason);
    }

    @Override
    public void delHistoryTask(String processInstanceId) {
        HistoryService historyService = processEngine.getHistoryService();
        historyService.deleteHistoricProcessInstance(processInstanceId);
    }

}

